大家好!在前面的文章中,我們已經完成了交易紀錄管理、銀行帳戶管理和分類管理的功能。
今天,我們將繼續完善我們的個人財務管理系統,專注於報表功能的開發。
報表功能是財務管理系統中非常重要的一環,透過報表,我們可以直觀地了解自己的財務狀況,分析收入與支出的趨勢,從而做出更明智的財務決策。
接下來,我們將實作以下內容:
我們將使用 Chart.js 來實現圖表的呈現。首先,安裝相關的套件。
npm install vue-chartjs chart.js
在 pages/ 目錄下建立 reports.vue 檔案。
touch pages/reports.vue
在 reports.vue 中,我們建立基本的頁面結構。
<template>
<div>
<h2 class="text-2xl font-bold mb-4">財務報表</h2>
<div v-if="error" class="text-red-500">
{{ error }}
</div>
<div v-else>
<!-- 篩選條件 -->
<div class="mb-4">
<label>選擇日期範圍:</label>
<input type="date" v-model="startDate" /> -
<input type="date" v-model="endDate" />
<button @click="fetchReportData" class="bg-blue-600 text-white px-4 py-2 ml-2">更新報表</button>
</div>
<!-- 報表圖表 -->
<div>
<canvas id="incomeExpenseChart"></canvas>
</div>
<!-- 分類支出圓餅圖 -->
<div class="mt-8">
<canvas id="categoryExpenseChart"></canvas>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Chart } from 'chart.js/auto'
const error = ref(null)
const startDate = ref('')
const endDate = ref('')
let chartInstance = null
let pieChartInstance = null
const fetchReportData = async () => {
error.value = null
try {
const data = await fetchReportAPI()
updateChart(data)
updatePieChart(data.categoryExpense)
} catch (err) {
error.value = '無法獲取報表資料,請稍後再試。'
console.error('Error fetching report data:', err)
}
}
const fetchReportAPI = async () => {
const params = {}
if (startDate.value) params.start_date = startDate.value
if (endDate.value) params.end_date = endDate.value
const response = await $fetch('/api/reports', {
method: 'GET',
params,
})
if (response.status === 'success') {
return response
} else {
throw new Error(response.message || '無法獲取報表資料')
}
}
const updateChart = (data) => {
const ctx = document.getElementById('incomeExpenseChart').getContext('2d')
if (chartInstance) {
chartInstance.destroy()
}
chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: data.labels,
datasets: [
{
label: '收入',
data: data.income,
backgroundColor: 'rgba(75, 192, 192, 0.5)',
},
{
label: '支出',
data: data.expense,
backgroundColor: 'rgba(255, 99, 132, 0.5)',
},
],
},
options: {
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
},
},
},
})
}
const updatePieChart = (categoryData) => {
const ctx = document.getElementById('categoryExpenseChart').getContext('2d')
if (pieChartInstance) {
pieChartInstance.destroy()
}
pieChartInstance = new Chart(ctx, {
type: 'pie',
data: {
labels: categoryData.labels,
datasets: [
{
data: categoryData.data,
backgroundColor: categoryData.colors,
},
],
},
options: {
responsive: true,
},
})
}
onMounted(() => {
const today = new Date()
startDate.value = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().substr(0, 10)
endDate.value = today.toISOString().substr(0, 10)
fetchReportData()
})
</script>
<style scoped>
/* 頁面專屬的樣式 */
</style>
補充後端的部分,後端 API 的實作這裡簡要說明。
在後端框架中(例如 Laravel),新增一個報表的 API 路由。
Route::middleware('auth:api')->get('/reports', [ReportController::class, 'index']);
在 ReportController 中,根據日期範圍,查詢交易紀錄,並按月彙總收入與支出。
public function index(Request $request)
{
$user = $request->user();
$startDate = $request->query('start_date');
$endDate = $request->query('end_date');
$transactions = Transaction::where('user_id', $user->id)
->whereBetween('date', [$startDate, $endDate])
->get()
->groupBy(function($item) {
return Carbon::parse($item->date)->format('Y-m');
});
$labels = [];
$incomeData = [];
$expenseData = [];
foreach ($transactions as $month => $items) {
$labels[] = $month;
$income = $items->where('type', 'income')->sum('amount');
$expense = $items->where('type', 'expense')->sum('amount');
$incomeData[] = $income;
$expenseData[] = $expense;
}
// 新增分類支出數據
$categoryExpenseData = Transaction::where('user_id', $user->id)
->whereBetween('date', [$startDate, $endDate])
->where('type', 'expense')
->groupBy('category_id')
->selectRaw('category_id, SUM(amount) as total')
->get();
$categoryLabels = [];
$categoryData = [];
$categoryColors = [];
foreach ($categoryExpenseData as $item) {
$category = Category::find($item->category_id);
if ($category) {
$categoryLabels[] = $category->name;
$categoryData[] = $item->total;
$categoryColors[] = '#' . substr(md5($category->name), 0, 6); // 隨機顏色
}
}
return response()->json([
'status' => 'success',
'labels' => $labels,
'income' => $incomeData,
'expense' => $expenseData,
'categoryExpense' => [
'labels' => $categoryLabels,
'data' => $categoryData,
'colors' => $categoryColors,
],
]);
}
說明:
我們已經在報表頁面中,添加了一個圓餅圖,顯示各分類的支出比例。
今天,我們成功地實作了報表功能,建立了財務報表頁面,並使用圖表視覺化地呈現數據。
大部分的實作也都告一段落了,剩下就是做細節的調整,其實前面的內容,拿來任何一個人都可以直接照著copy,但是要走到與別人不一樣的道路上,對於細節的著墨就要更講究,甚至是一個讓人可以買單的服務或是產品。